/*
 * =====================================================================================
 *
 *       Filename:  logger.h
 *
 *    Description:  通用log类定义模块
 *
 *        Version:  1.0
 *        Created:  2011年08月25日
 *       Revision:  none
 *       Compiler:  g++
 *
 *         Author:  mao.chen@duomi.com
 *        Company:  caiyunzaixian
 *
 * =====================================================================================
 */

#ifndef _DUOMI_COMMON_LOG_UTIL__
#define _DUOMI_COMMON_LOG_UTIL__

#include <string>
#include <map>
#include  <sys/shm.h>

// buffer大小
#define IOBUFFSIZE    65536

// 每800次检查log level从共享内存里读取一次(避免从共享内存里面读取过慢)
#define MAX_COUNTER   800

// log 级别的enum
enum {                                   
    LOGDEBUG = 0,                            // debug
    LOGINFO,                             // information
    LOGWARN,                             // warning
    LOGERROR                             // error
};

// log 类型的enum，每个类型对应一个log文件
enum {
    LOG_TYPE_PROGRAM = 0,
    LOG_TYPE_BUSINESS,
    LOG_TYPE_ERROR
};

// log 文件按时间分割的粒度
enum {
    LOG_INTERVAL_HOUR = 0,
    LOG_INTERVAL_DAY,
    LOG_INTERVAL_MONTH,
    LOG_INTERVAL_YEAR
};

typedef std::map<std::string,std::string> SSMAP;

class CLogger {
public:
    CLogger();
    ~CLogger();
    
    /*! @function ******************************************************************
    Name : Init
    Description : 初始化函数，读取配置文件
    Parameters :
       [IN] errorLogPrefix : 错误日志路径
       [IN] programLogPrefix : 程序日志路径
       [IN] businessLogPrefix : 业务日志路径
       [IN] logLevel : 日志级别(debug/info/warn/error)
       [IN] logInterval : 日志文件切分时间(hour/day/month/year)
       [IN] key : 日志级别修改所需的key
    Return : 正确返回0
    -------------------------------------------------------------------------------- 
    Remarks : 全局调用一次
    Usage : Init("/a8root/error", "/a8root/program", "/a8root/business", 
                      LOGDEBUG, LOG_INTERVAL_DAY, "test123")
    *******************************************************************************/
    int Init(const char* errorLogPrefix, const char* programLogPrefix, const char* businessLogPrefix,
                  int logLevel, int logInterval, const char* key);

    /*! @function ******************************************************************
    Name : ProgramLog
    Description : 记录程序日志
    Parameters :
       [IN] level : 日志级别
       [IN] format : 与printf一样的参数格式
    Return : 正确返回0
    -------------------------------------------------------------------------------- 
    Remarks : 如果级别不够当前设置的LOG级别，日志不会被记录
    Usage : ProgramLog(LOGDEBUG, "ErrorNum: %d, ErrorMessage: %s", 500, "内部错误");
    *******************************************************************************/
    int ProgramLog(int level, const char* format, ...);

    /*! @function ******************************************************************
    Name : BusinessLog
    Description : 记录业务日志
    Parameters :
       [IN] format : 与printf一样的参数格式
    Return : 正确返回0
    -------------------------------------------------------------------------------- 
    Remarks : 
    Usage : BusinessLog("后台接口访问时间: %d", 500);
    *******************************************************************************/
    int BusinessLog(const char* format, ...);
    
    /*! @function ******************************************************************
    Name : ChangeLogLevel
    Description : 处理http请求，修改log级别
    Parameters :
       [IN] uri : 服务的路径，目前没用上
       [IN] request : 请求的参数列表，需要包括:
                            level - int，范围定义在logger.h中
                            key - string，对应配置文件中的key
    Return : 正确返回0，并且会调整log到相应的级别
                 失败返回非0，log级别不会调整
    -------------------------------------------------------------------------------- 
    Remarks : 
    Usage : ProcessLogRequest("/common_log/", "level=1&key=test123")
    *******************************************************************************/
    int ChangeLogLevel(const char* uri, const char* request);
    
    /*! @function ******************************************************************
    Name : Release
    Description : 释放内部资源
    Parameters :
    Return : 
    -------------------------------------------------------------------------------- 
    Remarks : 
    Usage : 
    *******************************************************************************/
    void Release();

    /*! @function ******************************************************************
    Name : GetLogLevel
    Description : 获取共享内存中的LOG级别
    Parameters :
        [OUT] level : 日志级别
    Return : 成功返回0
    -------------------------------------------------------------------------------- 
    Remarks : 
    Usage : GetLogLevel(level);
    *******************************************************************************/
    int GetLogLevel(int& level);
 
    /*! @function ******************************************************************
    Name : SetLogLevel
    Description : 设置LOG级别共享内存
    Parameters :
        [IN] level : 日志级别
    Return : 成功返回0
    -------------------------------------------------------------------------------- 
    Remarks : 
    Usage : SetLogLevel(LOGINFO);
    *******************************************************************************/
    int SetLogLevel(int level);

private:
    // 全局的LOG级别的共享内存地址
    char* m_level_memblock;

    // 日志数据的buffer
    char* m_logBuf;
    // 参数的buffer
    char* m_dataBuf;

    // log 文件按时间分割的粒度
    int m_logInterval;
    // 配置文件中修改LOG级别时所需的key
    std::string m_key;

    // 配置文件中错误日志的前缀
    std::string m_errorLogPrefix;
    // 配置文件中程序日志的前缀
    std::string m_programLogPrefix;
    // 配置文件中业务日志的前缀
    std::string m_businessLogPrefix;
    
    // 错误日志的文件符
     int m_errorFd;
    // 程序日志的文件符
     int m_programFd;
    // 业务日志的文件符
     int m_businessFd;
    
    // 错误日志的创建时间
    struct tm m_errorLogTime;
    // 程序日志的创建时间
    struct tm m_programLogTime;
    // 业务日志的创建时间
    struct tm m_businessLogTime;
    
    /*! @function ******************************************************************
    Name : _ProgramLog
    Description : 记录程序日志
    Parameters :
        [IN] level : 日志级别
        [IN] logData : 日志信息
    Return : 成功返回0
    -------------------------------------------------------------------------------- 
    Remarks : 
    Usage : _ProgramLog(LOGINFO, "后台服务返回");
    *******************************************************************************/
    int _ProgramLog(int level, const char* logData);
    
    /*! @function ******************************************************************
    Name : _BusinessLog
    Description : 记录业务日志
    Parameters :
        [IN] logData : 日志信息
    Return : 成功返回0
    -------------------------------------------------------------------------------- 
    Remarks : 
    Usage : _BusinessLog("后台服务返回");
    *******************************************************************************/
    int _BusinessLog(const char* logData);
    
    /*! @function ******************************************************************
    Name : _GetLogFd
    Description : 获取日志文件描述符
    Parameters :
        [IN] logType : 日志类型
        [IN] currentTime : 当前时间
    Return : 成功返回0
    -------------------------------------------------------------------------------- 
    Remarks : 根据logType返回相应的文件描述符。
                    如果新建了日志文件，则返回新的描述符。
    Usage : _GetLogFd(LOG_TYPE_PROGRAM, time);
    *******************************************************************************/
    int _GetLogFd(int logType, const struct tm& currentTime);
    
    /*! @function ******************************************************************
    Name : _NeedNewLog
    Description : 判断是否需要新建日志文件
    Parameters :
        [IN] currentTime : 当前时间
        [IN] logTime : 老日志创建时间
    Return : 需要新建返回true，否则返回false
    -------------------------------------------------------------------------------- 
    Remarks : 根据config里面配置的日志文件切分时间粒度和当前时
                    间决定是否需要新建日志。
    Usage : _NeedNewLog(currentTime, logTime);
    *******************************************************************************/
    bool _NeedNewLog(const struct tm& currentTime, const struct tm& logTime);

    /*! @function ******************************************************************
    Name : _OpenLog
    Description : 打开日志文件
    Parameters :
        [IN] prefix : 日志文件前缀
        [IN] currentTime : 当前时间
    Return : 成功返回0
    -------------------------------------------------------------------------------- 
    Remarks : 以追加的形式打开文件。
                    按照config的配置的时间粒度，将相应的时间放入文件名
    Usage : _OpenLog("/a8root/error", time);
    *******************************************************************************/
    int _OpenLog(const std::string& prefix, const struct tm& currentTime);

    /*! @function ******************************************************************
    Name : _WriteLog
    Description :记录日志
    Parameters :
        [IN] fd : 日志文件描述符
        [IN] buf : 日志信息
        [IN] size : buf大小
    Return : 成功返回0
    -------------------------------------------------------------------------------- 
    Remarks : 
    Usage : _WriteLog(435, "示例log", 7);
    *******************************************************************************/
    static int _WriteLog(int fd, const void *buf, size_t size);

    // TODO(mao): move to HTTP parser lib
    static SSMAP ParseHttpGetParam(const std::string& request);
};
#endif
